/*
 * Sylvain BERTRAND <digital.ragnarok@gmail.com>
 * code protected by GNU GPL v3
 */
#include <X11/Xlib.h>
#include <X11/extensions/sync.h>
#include <glib.h>
#include <libxml/xinclude.h>
#include "../../toolkit/keyboard.h"
#include "../../toolkit/xml.h"
#include "../misc.h"
#include "../geom.h"
#include "../frame.h"
#include "../actions.h"
#include "../window.h"
#include "../stacking.h"
#include "../client.h"
#include "../config.h"
#include "../place.h"
#include "../moveresize.h"
#include "../screen.h"
enum {
	CURRENT_MONITOR = -1,
	ALL_MONITORS = -2,
	NEXT_MONITOR = -3,
	PREV_MONITOR = -4
};
struct options {
	struct wm_gravity_coord x;
	struct wm_gravity_coord y;
	gint w;
	gint w_denom;
	gint h;
	gint h_denom;
	gint monitor;
};

static gpointer setup_func(xmlNodePtr node)
{
	xmlNodePtr n;
	struct options *o;
	o = g_slice_new0(struct options);
	o->x.pos = G_MININT;
	o->y.pos = G_MININT;
	o->w = G_MININT;
	o->h = G_MININT;
	o->monitor = CURRENT_MONITOR;
	if((n = t_xml_find_node(node, "x")))
		config_parse_gravity_coord(n, &o->x);
	if((n = t_xml_find_node(node, "y")))
		config_parse_gravity_coord(n, &o->y);
	if((n = t_xml_find_node(node, "width"))) {
		gchar *s = t_xml_node_string(n);
		if(g_ascii_strcasecmp(s, "current") != 0)
			config_parse_relative_number(s, &o->w, &o->w_denom);
		g_free(s);
	}
	if((n = t_xml_find_node(node, "height"))) {
		gchar *s = t_xml_node_string(n);
		if(g_ascii_strcasecmp(s, "current") != 0)
			config_parse_relative_number(s, &o->h, &o->h_denom);
		g_free(s);
	}
	if((n = t_xml_find_node(node, "monitor"))) {
		gchar *s = t_xml_node_string(n);
		if(g_ascii_strcasecmp(s, "current") != 0) {
			if(!g_ascii_strcasecmp(s, "all"))
				o->monitor = ALL_MONITORS;
			else if(!g_ascii_strcasecmp(s, "next"))
				o->monitor = NEXT_MONITOR;
			else if(!g_ascii_strcasecmp(s, "prev"))
				o->monitor = PREV_MONITOR;
			else
				o->monitor = t_xml_node_int(n) - 1;
		}
		g_free(s);
	}
	return o;
}

static void free_func(gpointer o)
{
	g_slice_free(struct options, o);
}

static gboolean run_func(struct wm_actions_data *data, gpointer options)
{
	struct options *o = options;
	if(data->client) {
		struct wm_rect *area, *carea;
		struct wm_client *c;
		guint mon, cmon;
		gint x, y, lw, lh, w, h;
		c = data->client;
		mon = o->monitor;
		cmon = client_monitor(c);
		switch (mon) {
		case CURRENT_MONITOR:
			mon = cmon;
			break;
		case ALL_MONITORS:
			mon = SCREEN_AREA_ALL_MONITORS;
			break;
		case NEXT_MONITOR:
			mon = (cmon + 1 > screen_num_monitors - 1) ? 0 : (cmon + 1);
			break;
		case PREV_MONITOR:
			mon = (cmon == 0) ? (screen_num_monitors - 1) : (cmon - 1);
			break;
		default:
			g_assert_not_reached();
		}
		area = screen_area(c->desktop, mon, NULL);
		carea = screen_area(c->desktop, cmon, NULL);
		w = o->w;
		if(w == G_MININT)
			w = c->area.width;
		else if(o->w_denom)
			w = (w * area->width) / o->w_denom;
		h = o->h;
		if(h == G_MININT)
			h = c->area.height;
		else if(o->h_denom)
			h = (h * area->height) / o->h_denom;
		x = c->area.x;
		y = c->area.y;
		client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE);
		w += c->frame->size.left + c->frame->size.right;
		h += c->frame->size.top + c->frame->size.bottom;
		x = o->x.pos;
		if(o->x.denom)
			x = (x * area->width) / o->x.denom;
		if(o->x.center)
			x = (area->width - w) / 2;
		else if(x == G_MININT)
			x = c->frame->area.x - carea->x;
		else if(o->x.opposite)
			x = area->width - w - x;
		x += area->x;
		y = o->y.pos;
		if(o->y.denom)
			y = (y * area->height) / o->y.denom;
		if(o->y.center)
			y = (area->height - h) / 2;
		else if(y == G_MININT)
			y = c->frame->area.y - carea->y;
		else if(o->y.opposite)
			y = area->height - h - y;
		y += area->y;
		w -= c->frame->size.left + c->frame->size.right;
		h -= c->frame->size.top + c->frame->size.bottom;
		client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE);
		client_find_onscreen(c, &x, &y, w, h, mon != cmon);
		actions_client_move(data, TRUE);
		client_configure(c, x, y, w, h, TRUE, TRUE, FALSE);
		actions_client_move(data, FALSE);
		g_slice_free(struct wm_rect, area);
		g_slice_free(struct wm_rect, carea);
	}
	return FALSE;
}

static gpointer setup_center_func(xmlNodePtr node)
{
	struct options *o;
	(void)node;
	o = g_slice_new0(struct options);
	o->x.pos = G_MININT;
	o->y.pos = G_MININT;
	o->w = G_MININT;
	o->h = G_MININT;
	o->monitor = -1;
	o->x.center = TRUE;
	o->y.center = TRUE;
	return o;
}

void action_moveresizeto_startup(void)
{
	actions_register("MoveResizeTo", setup_func, free_func, run_func);
	actions_register("MoveToCenter", setup_center_func, free_func, run_func);
}
